package com.che.software.testato.web.controller;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;

import org.apache.log4j.Logger;
import org.primefaces.context.RequestContext;
import org.primefaces.event.SelectEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.che.software.testato.business.HierarchyManager;
import com.che.software.testato.business.ProjectManager;
import com.che.software.testato.business.UserManager;
import com.che.software.testato.business.VersionManager;
import com.che.software.testato.business.exception.HierarchyCreationManagerException;
import com.che.software.testato.business.exception.HierarchySearchManagerException;
import com.che.software.testato.business.exception.ProjectSearchManagerException;
import com.che.software.testato.business.exception.UserSearchManagerException;
import com.che.software.testato.business.exception.VersionSearchManagerException;
import com.che.software.testato.domain.entity.Hierarchy;
import com.che.software.testato.domain.entity.Project;
import com.che.software.testato.domain.entity.User;
import com.che.software.testato.domain.entity.Version;
import com.che.software.testato.domain.entity.creation.HierarchyCreation;
import com.che.software.testato.domain.entity.search.HierarchySearch;
import com.che.software.testato.domain.entity.search.ProjectSearch;
import com.che.software.testato.domain.entity.search.VersionSearch;
import com.che.software.testato.web.controller.acontroller.AbstractController;

/**
 * Controller used to manage the hierarchies.
 * 
 * @author Clement HELIOU (clement.heliou@che-software.com).
 * @copyright Che Software.
 * @license GNU General Public License.
 * @see AbstractController, Serializable.
 * @since July, 2011.
 * 
 *        This file is part of Testato.
 * 
 *        Testato is free software: you can redistribute it and/or modify it
 *        under the terms of the GNU General Public License as published by the
 *        Free Software Foundation, either version 3 of the License, or (at your
 *        option) any later version.
 * 
 *        Testato is distributed in the hope that it will be useful, but WITHOUT
 *        ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *        FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 *        for more details.
 * 
 *        You should have received a copy of the GNU General Public License
 *        along with Testato. If not, see <http://www.gnu.org/licenses/>.
 * 
 *        Testato's logo is a creation of Arrioch
 *        (http://arrioch.deviantart.com/) and it's distributed under the terms
 *        of the Creative Commons License.
 */
@Component("hierarchiesController")
@ManagedBean(name = "hierarchiesController")
@Scope("session")
@SessionScoped
public class HierarchiesController extends AbstractController implements Serializable {

	/**
	 * Constants.
	 */
	private static final Logger LOGGER = Logger.getLogger(HierarchiesController.class);
	private static final long serialVersionUID = -6487646290702866850L;

	/**
	 * Members.
	 */
	private Boolean checkExistingHierarchy = false;
	private HierarchyCreation hierarchyToCreate;
	private Hierarchy[] selectedHierarchies;
	@Autowired
	private transient HierarchyManager hierarchyManager;
	private List<Hierarchy> hierarchies;
	private List<SelectItem> projects, responsableUsers, versions;
	@Autowired
	private transient ProjectManager projectManager;
	@Autowired
	private transient UserManager userManager;
	@Autowired
	private transient VersionManager versionManager;

	/**
	 * Creating the object corresponding to the hierarchy to create.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @param ev the event launched during the creation of a hierarchy.
	 * @since July, 2011.
	 */
	public void createHierarchy(ActionEvent ev) {
		LOGGER.debug("createHierarchy(" + hierarchyToCreate.getHighLevelGoal() + ").");
		if (0 == hierarchyToCreate.getProject().getProjectId() || 0 == hierarchyToCreate.getVersion().getVersionId() || 0 == hierarchyToCreate.getUser().getUserId()) {
			RequestContext.getCurrentInstance().addCallbackParam(NOT_VALID_CALLBACK_PARAM, true);
		} else {
			try {
				checkExistingHierarchy = hierarchyManager.isHierarchyExistingFromVersionId(hierarchyToCreate.getVersion().getVersionId());
				if (!checkExistingHierarchy) {
					hierarchyManager.createHierarchyFromBean(hierarchyToCreate);
					hierarchyToCreate = new HierarchyCreation();
					hierarchies = null; // To trigger the data loading.
					RequestContext.getCurrentInstance().addPartialUpdateTarget("hierarchiesForm:hierarchiesTable");
				} else { // A hierarchy is alreay existing for this version.
					RequestContext.getCurrentInstance().addCallbackParam(NOT_VALID_CALLBACK_PARAM, true);
				}
			} catch (HierarchySearchManagerException e) {
				LOGGER.error("Error during the hierarchy existance checking.", e);
				RequestContext.getCurrentInstance().addCallbackParam(NOT_VALID_CALLBACK_PARAM, true);
			} catch (HierarchyCreationManagerException e) {
				LOGGER.error("Error during the creation of a hierarchy.", e);
				RequestContext.getCurrentInstance().addCallbackParam(NOT_VALID_CALLBACK_PARAM, true);
			}
		}
	}

	/**
	 * Initializing the version list to trigger the data loading.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @param e the event launched.
	 * @since July, 2011.
	 */
	public void handleSelect(SelectEvent e) {
		LOGGER.debug("handleSelect(" + e.getObject().toString() + ").");
		versions = null;
	}

	/**
	 * Initializing the object corresponding to the hierarchy to create.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @param e the event launched during the creation of a hierarchy.
	 * @since July, 2011.
	 */
	public void initHierarchyToCreate(ActionEvent e) {
		LOGGER.debug("initHierarchyToCreate().");
		hierarchyToCreate = new HierarchyCreation();
	}

	/**
	 * Redirection to perform to get the hierarchies page.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @return the resulting redirection.
	 * @since August, 2011.
	 */
	public String onAccess() {
		LOGGER.debug("onAccess().");
		hierarchies = null; // To trigger the data loading.
		projects = null;
		responsableUsers = null;
		selectedHierarchies = null;
		versions = null;
		return "hierarchies.xhtml?faces-redirect=true";
	}

	/**
	 * Redirection to perform when the user wants to visualize a hierarchy.
	 * 
	 * @author Clement HELIOU (clement.heliou@che-software.com).
	 * @return the resulting redirection.
	 * @since July, 2011.
	 */
	public String onVisualize() {
		LOGGER.debug("onVisualize().");
		return "hierarchyVisualization.xhtml?faces-redirect=true";
	}

	/**
	 * Getter for the private field value hierarchyManager.
	 * 
	 * @return the hierarchyManager field value.
	 */
	public HierarchyManager getHierarchyManager() {
		return hierarchyManager;
	}

	/**
	 * Setting a value to the hierarchyManager field.
	 * 
	 * @param hierarchyManager the value to set.
	 */
	public void setHierarchyManager(HierarchyManager hierarchyManager) {
		this.hierarchyManager = hierarchyManager;
	}

	/**
	 * Getter for the private field value hierarchies.
	 * 
	 * @return the hierarchies field value.
	 */
	public List<Hierarchy> getHierarchies() {
		if (null == hierarchies) {
			LOGGER.debug("getHierarchies(): initialization.");
			try {
				List<Hierarchy> temporaryHierarchies = hierarchyManager.searchHierarchy(new HierarchySearch());
				for (Hierarchy hierarchy : temporaryHierarchies) {
					hierarchy.setVersion(versionManager.searchVersion(new VersionSearch(hierarchy.getVersionId())).get(0));
					hierarchy.setProject(projectManager.searchProject(new ProjectSearch(hierarchy.getVersion().getProjectId())).get(0));
					hierarchy.setUser(userManager.searchUserFromId(hierarchy.getResponsableUserId()));
				}
				hierarchies = temporaryHierarchies;
			} catch (HierarchySearchManagerException e) {
				LOGGER.error("Error during the recovery of the hierarchies.", e);
			} catch (VersionSearchManagerException e) {
				LOGGER.error("Error during the recovery of a version.", e);
			} catch (ProjectSearchManagerException e) {
				LOGGER.error("Error during the recovery of a project.", e);
			} catch (UserSearchManagerException e) {
				LOGGER.error("Error during the recovery of an user.", e);
			}
		}
		return hierarchies;
	}

	/**
	 * Setting a value to the hierarchies field.
	 * 
	 * @param hierarchies the value to set.
	 */
	public void setHierarchies(List<Hierarchy> hierarchies) {
		this.hierarchies = hierarchies;
	}

	/**
	 * Getter for the private field value hierarchyToCreate.
	 * 
	 * @return the hierarchyToCreate field value.
	 */
	public HierarchyCreation getHierarchyToCreate() {
		return hierarchyToCreate;
	}

	/**
	 * Setting a value to the hierarchyToCreate field.
	 * 
	 * @param hierarchyToCreate the value to set.
	 */
	public void setHierarchyToCreate(HierarchyCreation hierarchyToCreate) {
		this.hierarchyToCreate = hierarchyToCreate;
	}

	/**
	 * Getter for the private field value selectedHierarchies.
	 * 
	 * @return the selectedHierarchies field value.
	 */
	public Hierarchy[] getSelectedHierarchies() {
		return selectedHierarchies;
	}

	/**
	 * Setting a value to the selectedHierarchies field.
	 * 
	 * @param selectedHierarchies the value to set.
	 */
	public void setSelectedHierarchies(Hierarchy[] selectedHierarchies) {
		this.selectedHierarchies = selectedHierarchies;
	}

	/**
	 * Getter for the private field value projectManager.
	 * 
	 * @return the projectManager field value.
	 */
	public ProjectManager getProjectManager() {
		return projectManager;
	}

	/**
	 * Setting a value to the projectManager field.
	 * 
	 * @param projectManager the value to set.
	 */
	public void setProjectManager(ProjectManager projectManager) {
		this.projectManager = projectManager;
	}

	/**
	 * Getter for the private field value versionManager.
	 * 
	 * @return the versionManager field value.
	 */
	public VersionManager getVersionManager() {
		return versionManager;
	}

	/**
	 * Setting a value to the versionManager field.
	 * 
	 * @param versionManager the value to set.
	 */
	public void setVersionManager(VersionManager versionManager) {
		this.versionManager = versionManager;
	}

	/**
	 * Getter for the private field value userManager.
	 * 
	 * @return the userManager field value.
	 */
	public UserManager getUserManager() {
		return userManager;
	}

	/**
	 * Setting a value to the userManager field.
	 * 
	 * @param userManager the value to set.
	 */
	public void setUserManager(UserManager userManager) {
		this.userManager = userManager;
	}

	/**
	 * Getter for the private field value checkExistingHierarchy.
	 * 
	 * @return the checkExistingHierarchy field value.
	 */
	public Boolean getCheckExistingHierarchy() {
		return checkExistingHierarchy;
	}

	/**
	 * Setting a value to the checkExistingHierarchy field.
	 * 
	 * @param checkExistingHierarchy the value to set.
	 */
	public void setCheckExistingHierarchy(Boolean checkExistingHierarchy) {
		this.checkExistingHierarchy = checkExistingHierarchy;
	}

	/**
	 * Getter for the private field value projects.
	 * 
	 * @return the projects field value.
	 */
	public List<SelectItem> getProjects() {
		if (null == projects) {
			LOGGER.debug("getProjects(): initialization.");
			List<SelectItem> result = new ArrayList<SelectItem>();
			try {
				for (Project project : projectManager.searchProject(new ProjectSearch())) {
					result.add(new SelectItem(project.getProjectId(), project.getName()));
				}
				projects = result;
			} catch (ProjectSearchManagerException e) {
				LOGGER.error("Error during the projects recovery.", e);
			}
		}
		return projects;
	}

	/**
	 * Setting a value to the projects field.
	 * 
	 * @param projects the value to set.
	 */
	public void setProjects(List<SelectItem> projects) {
		this.projects = projects;
	}

	/**
	 * Getter for the private field value responsableUsers.
	 * 
	 * @return the responsableUsers field value.
	 */
	public List<SelectItem> getResponsableUsers() {
		if (null == responsableUsers) {
			LOGGER.debug("getResponsableUsers(): initialization.");
			List<SelectItem> result = new ArrayList<SelectItem>();
			try {
				for (User user : userManager.searchResponsableUser()) {
					result.add(new SelectItem(user.getUserId(), user.getLastName() + " " + user.getName()));
				}
				responsableUsers = result;
			} catch (UserSearchManagerException e) {
				LOGGER.error("Error during the responsable users recovery.", e);
			}
		}
		return responsableUsers;
	}

	/**
	 * Setting a value to the responsableUsers field.
	 * 
	 * @param responsableUsers the value to set.
	 */
	public void setResponsableUsers(List<SelectItem> responsableUsers) {
		this.responsableUsers = responsableUsers;
	}

	/**
	 * Getter for the private field value versions.
	 * 
	 * @return the versions field value.
	 */
	public List<SelectItem> getVersions() {
		if (null == versions) {
			LOGGER.debug("getVersions(): initialization.");
			List<SelectItem> result = new ArrayList<SelectItem>();
			VersionSearch searchBean = new VersionSearch();
			if (null != hierarchyToCreate && null != hierarchyToCreate.getProject() && 0 != hierarchyToCreate.getProject().getProjectId()) {
				searchBean.setProjectId(hierarchyToCreate.getProject().getProjectId());
			}
			try {
				for (Version version : versionManager.searchVersion(searchBean)) {
					result.add(new SelectItem(version.getVersionId(), version.getName()));
				}
				versions = result;
			} catch (VersionSearchManagerException e) {
				LOGGER.error("Error during the version recovery.", e);
			}
		}
		return versions;
	}

	/**
	 * Setting a value to the versions field.
	 * 
	 * @param versions the value to set.
	 */
	public void setVersions(List<SelectItem> versions) {
		this.versions = versions;
	}
}